<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 6.3.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"chuangges.gitee.io","root":"/","scheme":"Pisces","version":"7.8.0","exturl":false,"sidebar":{"position":"right","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":"mac"},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="源码实现、网络相关、设计模式、算法相关、Nodejs">
<meta property="og:type" content="article">
<meta property="og:title" content="面试题之提升篇">
<meta property="og:url" content="https://chuangges.gitee.io/2020/04/05/13-%E9%9D%A2%E8%AF%95%E9%A2%98%E4%B9%8B%E6%8F%90%E5%8D%87%E7%AF%87/index.html">
<meta property="og:site_name" content="闯哥要去流浪了">
<meta property="og:description" content="源码实现、网络相关、设计模式、算法相关、Nodejs">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2020-04-05T15:06:25.000Z">
<meta property="article:modified_time" content="2023-12-13T09:04:08.953Z">
<meta property="article:author" content="chuangges">
<meta property="article:tag" content="面试题库">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="https://chuangges.gitee.io/2020/04/05/13-%E9%9D%A2%E8%AF%95%E9%A2%98%E4%B9%8B%E6%8F%90%E5%8D%87%E7%AF%87/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>面试题之提升篇 | 闯哥要去流浪了</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

<link rel="alternate" href="/atom.xml" title="闯哥要去流浪了" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">闯哥要去流浪了</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">愿你岁月可回首，且以深情共白头</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://chuangges.gitee.io/2020/04/05/13-%E9%9D%A2%E8%AF%95%E9%A2%98%E4%B9%8B%E6%8F%90%E5%8D%87%E7%AF%87/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/chuang.png">
      <meta itemprop="name" content="chuangges">
      <meta itemprop="description" content="chuangge's blog.">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="闯哥要去流浪了">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          面试题之提升篇
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2020-04-05 23:06:25" itemprop="dateCreated datePublished" datetime="2020-04-05T23:06:25+08:00">2020-04-05</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2023-12-13 17:04:08" itemprop="dateModified" datetime="2023-12-13T17:04:08+08:00">2023-12-13</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E9%9D%A2%E8%AF%95%E9%A2%98%E5%BA%93/" itemprop="url" rel="index"><span itemprop="name">面试题库</span></a>
                </span>
            </span>

          <br>
            <span class="post-meta-item" title="本文字数">
              <span class="post-meta-item-icon">
                <i class="far fa-file-word"></i>
              </span>
                <span class="post-meta-item-text">本文字数：</span>
              <span>23k</span>
            </span>
            <span class="post-meta-item" title="阅读时长">
              <span class="post-meta-item-icon">
                <i class="far fa-clock"></i>
              </span>
                <span class="post-meta-item-text">阅读时长 &asymp;</span>
              <span>21 分钟</span>
            </span>
            <div class="post-description">源码实现、网络相关、设计模式、算法相关、Nodejs</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h1 id="一、源码实现"><a href="#一、源码实现" class="headerlink" title="一、源码实现"></a>一、源码实现</h1><h2 id="数组扁平化"><a href="#数组扁平化" class="headerlink" title="数组扁平化"></a>数组扁平化</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 实现一个方法使多维数组变成一维数组</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">flatter</span>(<span class="params">arr</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!arr.<span class="property">length</span>) <span class="keyword">return</span>;</span><br><span class="line">    <span class="keyword">while</span> (arr.<span class="title function_">some</span>(<span class="function">(<span class="params">item</span>) =&gt;</span> <span class="title class_">Array</span>.<span class="title function_">isArray</span>(item))) &#123;</span><br><span class="line">        arr = [].<span class="title function_">concat</span>(...arr);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> arr;</span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">flatter</span>([<span class="number">1</span>, <span class="number">2</span>, [<span class="number">1</span>, [<span class="number">2</span>, <span class="number">3</span>, [<span class="number">4</span>, <span class="number">5</span>, [<span class="number">6</span>]]]]])</span><br></pre></td></tr></table></figure>


<h2 id="函数柯里化"><a href="#函数柯里化" class="headerlink" title="函数柯里化"></a>函数柯里化</h2><blockquote>
<p>指将多个参数的函数拆解为单一参数的多个函数的依次调用，即使用闭包返回一个延迟执行函数。优点是可以实现延迟计算和参数复用。</p>
</blockquote>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 普通函数 add(1, 2)、柯里化函数 currying(1)(2)</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">add</span> = (<span class="params">a, b</span>) =&gt; a + b</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">curry</span> = x =&gt; &#123; <span class="keyword">return</span> <span class="function"><span class="params">y</span> =&gt;</span> &#123; <span class="keyword">return</span> x + y &#125; &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 柯里化封装</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">curry</span> (fn) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">function</span> <span class="title function_">nest</span>(<span class="params">...args</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (args.<span class="property">length</span> === fn.<span class="property">length</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="title function_">fn</span>(...args);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">function</span> (<span class="params">arg</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="title function_">nest</span>(...args, arg);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> addCurry = <span class="title function_">curry</span>(add);</span><br><span class="line"><span class="keyword">const</span> num = <span class="title function_">addCurry</span>(<span class="number">2</span>, <span class="number">3</span>);</span><br></pre></td></tr></table></figure>


<h2 id="发布订阅模式"><a href="#发布订阅模式" class="headerlink" title="发布订阅模式"></a>发布订阅模式</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 实现一个发布订阅模式拥有 on emit once off 方法</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">EventEmitter</span> &#123;</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">events</span> = &#123;&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 实现订阅</span></span><br><span class="line">    <span class="title function_">on</span>(<span class="params">type, callBack</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="variable language_">this</span>.<span class="property">events</span>[type]) &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">events</span>[type] = [callBack];</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">events</span>[type].<span class="title function_">push</span>(callBack);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 删除订阅</span></span><br><span class="line">    <span class="title function_">off</span>(<span class="params">type, callBack</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> events = <span class="variable language_">this</span>.<span class="property">events</span>[type]</span><br><span class="line">        <span class="keyword">if</span> (!events) <span class="keyword">return</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">events</span>[type] = events.<span class="title function_">filter</span>(<span class="function"><span class="params">item</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> item !== callBack;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 只执行一次订阅事件</span></span><br><span class="line">    <span class="title function_">once</span>(<span class="params">type, callBack</span>) &#123;</span><br><span class="line">        <span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>) &#123;</span><br><span class="line">            <span class="title function_">callBack</span>();</span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">off</span>(type, fn);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">on</span>(type, fn);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 触发事件</span></span><br><span class="line">    <span class="title function_">emit</span>(<span class="params">type, ...rest</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="variable language_">this</span>.<span class="property">events</span>[type]) <span class="keyword">return</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">events</span>[type].<span class="title function_">forEach</span>(<span class="function"><span class="params">fn</span> =&gt;</span> fn.<span class="title function_">apply</span>(<span class="variable language_">this</span>, rest));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用如下</span></span><br><span class="line"><span class="keyword">const</span> event = <span class="keyword">new</span> <span class="title class_">EventEmitter</span>();</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">handle</span> = (<span class="params">...rest</span>) =&gt; &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(rest);</span><br><span class="line">&#125;;</span><br><span class="line">event.<span class="title function_">on</span>(<span class="string">&quot;click&quot;</span>, handle);</span><br><span class="line">event.<span class="title function_">emit</span>(<span class="string">&quot;click&quot;</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>);</span><br><span class="line">event.<span class="title function_">off</span>(<span class="string">&quot;click&quot;</span>, handle);</span><br><span class="line">event.<span class="title function_">once</span>(<span class="string">&quot;dbClick&quot;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">123456</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h2 id="列表和树形互转"><a href="#列表和树形互转" class="headerlink" title="列表和树形互转"></a>列表和树形互转</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> array = [</span><br><span class="line">    &#123; <span class="attr">id</span>: <span class="number">1</span>, <span class="attr">text</span>: <span class="string">&#x27;节点1&#x27;</span>, <span class="attr">parentId</span>: <span class="number">0</span> &#125;,</span><br><span class="line">    &#123; <span class="attr">id</span>: <span class="number">2</span>, <span class="attr">text</span>: <span class="string">&#x27;节点1-1&#x27;</span>, <span class="attr">parentId</span>: <span class="number">1</span> &#125;,</span><br><span class="line">]</span><br><span class="line"><span class="keyword">let</span> treeData = <span class="title function_">listToTree</span>(array)</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">listToTree</span>(<span class="params">data</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> temp = &#123;&#125;;</span><br><span class="line">    <span class="keyword">let</span> treeData = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; data.<span class="property">length</span>; i++) &#123;</span><br><span class="line">        temp[data[i].<span class="property">id</span>] = data[i];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i <span class="keyword">in</span> temp) &#123;</span><br><span class="line">        <span class="keyword">if</span> (+temp[i].<span class="property">parentId</span> != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!temp[temp[i].<span class="property">parentId</span>].<span class="property">children</span>) &#123;</span><br><span class="line">                temp[temp[i].<span class="property">parentId</span>].<span class="property">children</span> = [];</span><br><span class="line">            &#125;</span><br><span class="line">            temp[temp[i].<span class="property">parentId</span>].<span class="property">children</span>.<span class="title function_">push</span>(temp[i]);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            treeData.<span class="title function_">push</span>(temp[i]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> treeData;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">treeToList</span>(<span class="params">data</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> res = [];</span><br><span class="line">    <span class="keyword">const</span> <span class="title function_">dfs</span> = tree =&gt; &#123;</span><br><span class="line">        tree.<span class="title function_">forEach</span>(<span class="function"><span class="params">item</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (item.<span class="property">children</span>) &#123;</span><br><span class="line">                <span class="title function_">dfs</span>(item.<span class="property">children</span>);</span><br><span class="line">                <span class="keyword">delete</span> item.<span class="property">children</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            res.<span class="title function_">push</span>(item);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="title function_">dfs</span>(data);</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<h2 id="bind、call、apply"><a href="#bind、call、apply" class="headerlink" title="bind、call、apply"></a>bind、call、apply</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Function</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">MyCall</span> = <span class="keyword">function</span> (<span class="params">context = <span class="variable language_">window</span></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> args = [...<span class="variable language_">arguments</span>].<span class="title function_">slice</span>(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">let</span> fn = <span class="title class_">Symbol</span>();</span><br><span class="line">    context[fn] = <span class="variable language_">this</span>;</span><br><span class="line">    <span class="keyword">return</span> context.<span class="title function_">fn</span>(...args);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="title class_">Function</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">MyApply</span> = <span class="keyword">function</span> (<span class="params">context = <span class="variable language_">window</span></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> args = <span class="variable language_">arguments</span>[<span class="number">1</span>] || [];</span><br><span class="line">    <span class="keyword">let</span> fn = <span class="title class_">Symbol</span>();</span><br><span class="line">    context[fn] = <span class="variable language_">this</span>;</span><br><span class="line">    <span class="keyword">return</span> context[fn](...args);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="title class_">Function</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">MyBind</span> = <span class="keyword">function</span> (<span class="params">context = <span class="variable language_">window</span></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> fn = <span class="variable language_">this</span></span><br><span class="line">    <span class="keyword">const</span> args = <span class="title class_">Array</span>.<span class="title function_">from</span>(<span class="variable language_">arguments</span>).<span class="title function_">slice</span>(<span class="number">1</span>)</span><br><span class="line">    <span class="keyword">const</span> newArgs = args.<span class="title function_">concat</span>(...<span class="variable language_">arguments</span>)</span><br><span class="line">    <span class="keyword">const</span> newFn = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">        fn.<span class="title function_">apply</span>(<span class="variable language_">this</span> <span class="keyword">instanceof</span> newFn ? <span class="variable language_">this</span> : context, newArgs)</span><br><span class="line">    &#125;</span><br><span class="line">    newFn.<span class="property"><span class="keyword">prototype</span></span> = <span class="title class_">Object</span>.<span class="title function_">create</span>(fn.<span class="property"><span class="keyword">prototype</span></span>)</span><br><span class="line">    <span class="keyword">return</span> newFn</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 构造函数、普通函数调用</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params">name, age</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(name, age);  </span><br><span class="line">&#125;</span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">name</span> = <span class="string">&#x27;Lisy&#x27;</span></span><br><span class="line"><span class="keyword">let</span> obj = &#123; <span class="attr">name</span>: <span class="string">&#x27;Tom&#x27;</span>, <span class="attr">age</span>: <span class="number">10</span> &#125;</span><br><span class="line"><span class="keyword">let</span> bindFn = <span class="title class_">Person</span>.<span class="title class_">MyBind</span>(obj)</span><br><span class="line"><span class="keyword">let</span> _obj = <span class="keyword">new</span> <span class="title function_">bindFn</span>(<span class="string">&#x27;Mike&#x27;</span>, <span class="number">20</span>)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">111</span>, _obj.<span class="property">name</span>)</span><br></pre></td></tr></table></figure>


<h2 id="模拟-new"><a href="#模拟-new" class="headerlink" title="模拟 new"></a>模拟 new</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">myNew</span>(<span class="params">fn</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> newObj = <span class="title class_">Object</span>.<span class="title function_">create</span>(fn.<span class="property"><span class="keyword">prototype</span></span>);</span><br><span class="line">    result = fn.<span class="title function_">apply</span>(newObj, [...<span class="variable language_">arguments</span>].<span class="title function_">slice</span>(<span class="number">1</span>));</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">typeof</span> result === <span class="string">&quot;object&quot;</span> ? result : newObj;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<h2 id="axios"><a href="#axios" class="headerlink" title="axios"></a>axios</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fetch = <span class="built_in">require</span>(<span class="string">&quot;node-fetch&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> axiosOriginal = &#123;</span><br><span class="line">  <span class="attr">methods</span>: [<span class="string">&#x27;GET&#x27;</span>, <span class="string">&#x27;POST&#x27;</span>, <span class="string">&#x27;PUT&#x27;</span>, <span class="string">&#x27;DELETE&#x27;</span>, <span class="string">&#x27;OPTIONS&#x27;</span>]</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">const</span> axios = <span class="keyword">new</span> <span class="title class_">Proxy</span>(axiosOriginal, &#123;</span><br><span class="line">  <span class="title function_">set</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&quot;Can&#x27;t set any property&quot;</span>);</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  <span class="title function_">get</span>(<span class="params">target, name</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> method = name.<span class="title function_">toLocaleUpperCase</span>();</span><br><span class="line">    <span class="keyword">if</span> (target.<span class="property">methods</span>.<span class="title function_">indexOf</span>(method) === -<span class="number">1</span>) <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`Can&#x27;t support method <span class="subst">$&#123;method&#125;</span>`</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="function">(<span class="params">url, options</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="title function_">fetch</span>(url, &#123;</span><br><span class="line">        method,</span><br><span class="line">        ...options</span><br><span class="line">      &#125;).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> res.<span class="title function_">text</span>())</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">axios.<span class="title function_">get</span>(<span class="string">&quot;http://www.baidu.com&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(res));</span><br><span class="line">axios.<span class="title function_">post</span>(<span class="string">&quot;http://www.baidu.com&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(res));</span><br></pre></td></tr></table></figure>


<h2 id="Promise"><a href="#Promise" class="headerlink" title="Promise"></a>Promise</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="variable constant_">PENDING</span> = <span class="string">&quot;pending&quot;</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">RESOLVED</span> = <span class="string">&quot;resolved&quot;</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">REJECTED</span> = <span class="string">&quot;rejected&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyPromise</span> &#123;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params">fn</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">state</span> = <span class="variable constant_">PENDING</span>;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">resolvedHandlers</span> = [];</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">rejectedHandlers</span> = [];</span><br><span class="line">    <span class="title function_">fn</span>(<span class="variable language_">this</span>.<span class="property">resolve</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>), <span class="variable language_">this</span>.<span class="property">reject</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>));</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">resolve</span>(<span class="params">props</span>) &#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">state</span> = <span class="variable constant_">RESOLVED</span>;</span><br><span class="line">      <span class="keyword">const</span> resolveHandler = <span class="variable language_">this</span>.<span class="property">resolvedHandlers</span>.<span class="title function_">shift</span>();</span><br><span class="line">      <span class="keyword">if</span> (!resolveHandler) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">const</span> result = <span class="title function_">resolveHandler</span>(props);</span><br><span class="line">      <span class="keyword">if</span> (result &amp;&amp; result <span class="keyword">instanceof</span> <span class="title class_">MyPromise</span>) &#123;</span><br><span class="line">        result.<span class="title function_">then</span>(...<span class="variable language_">this</span>.<span class="property">resolvedHandlers</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">reject</span>(<span class="params">error</span>) &#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">state</span> = <span class="variable constant_">REJECTED</span>;</span><br><span class="line">      <span class="keyword">const</span> rejectHandler = <span class="variable language_">this</span>.<span class="property">rejectedHandlers</span>.<span class="title function_">shift</span>();</span><br><span class="line">      <span class="keyword">if</span> (!rejectHandler) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">const</span> result = <span class="title function_">rejectHandler</span>(error);</span><br><span class="line">      <span class="keyword">if</span> (result &amp;&amp; result <span class="keyword">instanceof</span> <span class="title class_">MyPromise</span>) &#123;</span><br><span class="line">        result.<span class="title function_">catch</span>(...<span class="variable language_">this</span>.<span class="property">rejectedHandlers</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">then</span>(<span class="params">...handlers</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">resolvedHandlers</span> = [...<span class="variable language_">this</span>.<span class="property">resolvedHandlers</span>, ...handlers];</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">catch</span>(...handlers) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">rejectedHandlers</span> = [...<span class="variable language_">this</span>.<span class="property">rejectedHandlers</span>, ...handlers];</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title class_">MyPromise</span>.<span class="property">all</span> = <span class="keyword">function</span> (<span class="params">promises</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> results = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; promises.<span class="property">length</span>; i++) &#123;</span><br><span class="line">      <span class="keyword">const</span> promise = promises[i];</span><br><span class="line">      promise.<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">        results.<span class="title function_">push</span>(res);</span><br><span class="line">        <span class="keyword">if</span> (results.<span class="property">length</span> === promises.<span class="property">length</span>) <span class="title function_">resolve</span>(results);</span><br><span class="line">      &#125;).<span class="title function_">catch</span>(reject);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title class_">MyPromise</span>.<span class="property">race</span> = <span class="keyword">function</span> (<span class="params">promises</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; promises.<span class="property">length</span>; i++) &#123;</span><br><span class="line">      <span class="keyword">const</span> promise = promises[i];</span><br><span class="line">      promise.<span class="title function_">then</span>(resolve).<span class="title function_">catch</span>(reject);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// promise.finally 实现</span></span><br><span class="line"><span class="title class_">Promise</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">finally</span> = <span class="keyword">function</span> (<span class="params">callback</span>) &#123;</span><br><span class="line">  <span class="keyword">let</span> P = <span class="variable language_">this</span>.<span class="property">constructor</span>;</span><br><span class="line">  <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">then</span>(</span><br><span class="line">    <span class="function"><span class="params">value</span>  =&gt;</span> P.<span class="title function_">resolve</span>(<span class="title function_">callback</span>()).<span class="title function_">then</span>(<span class="function">() =&gt;</span> value),</span><br><span class="line">    <span class="function"><span class="params">reason</span> =&gt;</span> P.<span class="title function_">resolve</span>(<span class="title function_">callback</span>()).<span class="title function_">then</span>(<span class="function">() =&gt;</span> &#123; <span class="keyword">throw</span> reason &#125;)</span><br><span class="line">  );</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>


<h2 id="React"><a href="#React" class="headerlink" title="React"></a>React</h2><ol>
<li>React 实现主要分为 Component、Element。</li>
<li>Component 属于 React 实例，创建实例的过程中会在实例中注册 state、props 属性，还会依次调用内置的生命周期函数。Component render 函数会返回一个 ElementObj &#x2F; null。</li>
<li>Element 对象分为原生 Element 对象和组件式对象，它们会被一起解析成虚拟 DOM 树，内部使用的 state、props 也会以 AST 的形式注入进去。</li>
<li>虚拟 DOM 树渲染前后会触发 componentWillMount、componentDidMount 等生命周期函数，解析完成后将被渲染成真实 DOM 树。</li>
<li>调用 setState 时会更新 Component state，并会触发内部的一个 updater。调用 render 会生成新的虚拟 DOM 树，利用 diff 算法对比后的醋最优方案去更新 DOM。</li>
</ol>
<h2 id="Redux"><a href="#Redux" class="headerlink" title="Redux"></a>Redux</h2><ul>
<li><strong>实现原理</strong>：创建一个状态中心，通过纯函数来对其数据进行修改，然后将修改结果以发布订阅的方式通知到所有订阅者，从而达到一个共享状态的效果。</li>
<li><strong>核心</strong>：函数式编程，使用无副作用的同步函数 action 来触发 reducer 对数据进行修改。</li>
<li><strong>创建函数</strong>：createStore，这个函数的作用是创建一个 store 对象，关键步骤如下：<ul>
<li>1、将 reducer 利用闭包存储在函数内。</li>
<li>2、导出 subscribe 函数，内部将订阅者添加到一个订阅者列表中并等待通知。</li>
<li>3、导出 dispatch 函数，内部执行 reducer 函数并将 action 作为参数传入到 reducer。</li>
<li>4、将 reducer 函数的返回结果存到 currentState，并通知所有订阅者 state 已更新。</li>
<li>5、订阅者可以通过 getState 得到最新的 state。</li>
</ul>
</li>
<li><strong>中间件原理</strong>：Decorator 装饰器模式，将中间件通过装饰后返回一个增强型的 dispatch。</li>
<li><strong>redux-thunk 实现异步</strong>：内部判断 action 的类型，是函数则将 dispatch 作为参数执行，action 函数决定何时（可能是异步操作后）执行 dispatch。</li>
</ul>
<h2 id="new-Vue-执行后"><a href="#new-Vue-执行后" class="headerlink" title="new Vue 执行后"></a>new Vue 执行后</h2><ol>
<li>new Vue 会调用 Vue 原型链上的 _init 方法对 Vue 实例进行初始化。</li>
<li>首先 initLifecycle 初始化生命周期，对 children、isMounted 等实例属性进行初始化。</li>
<li>initEvents，初始化当前实例上的一些自定义事件（Vue.$on）。</li>
<li>initRender，解析 slots 绑定在 Vue 实例上，绑定 createElement 方法在实例上。</li>
<li>完成对生命周期、自定义事件等属性的初始化后，触发生命周期钩子 beforeCreate。</li>
<li>initInjections，在初始化 data、props 之前完成依赖注入（类似于 React.Context）。</li>
<li>initState，初始化 data、props，并对内部属性完成数据劫持，更新时触发回调。</li>
<li>initProvide，对依赖注入进行解析。</li>
<li>完成对数据（state 状态）的初始化后，触发生命周期钩子 created。</li>
<li>进入挂载阶段，将 vue 模板语法通过 vue-loader 解析成虚拟 DOM 树，虚拟 DOM 树与数据完成双向绑定，触发生命周期钩子 beforeMount。</li>
<li>将解析好的虚拟 DOM 树通过 vue 渲染成真实 DOM，触发生命周期钩子 mounted。</li>
</ol>
<h2 id="webpack-热更新原理"><a href="#webpack-热更新原理" class="headerlink" title="webpack 热更新原理"></a>webpack 热更新原理</h2><ol>
<li>修改一个或多个文件时，文件系统会接收更改并通知 webpack 重新编译。</li>
<li>webpack 构建一或多个模块，并通知 HMR（Hot Module Replacement）服务器更新。</li>
<li>HMR Server 使用 Websocket 通知 HMR runtime 更新，runtime 通过 HTTP 更新 jsonp。</li>
<li>HMR runtime 替换更新中的模块，如果确定这些模块无法更新，则触发整个页面刷新。</li>
</ol>
<h1 id="二、网络相关"><a href="#二、网络相关" class="headerlink" title="二、网络相关"></a>二、网络相关</h1><h2 id="HTTP-状态码及其功能"><a href="#HTTP-状态码及其功能" class="headerlink" title="HTTP 状态码及其功能"></a>HTTP 状态码及其功能</h2><ul>
<li>100~199：信息提示</li>
<li>200~299：成功</li>
<li>300~399：重定向</li>
<li>400~499：客户端错误</li>
<li>500~599：服务端错误</li>
<li>200 成功</li>
<li>204 无内容</li>
<li>301 永久移动（回应 GET 响应时会自动将请求者转到新位置）</li>
<li>304 未修改（协商缓存）</li>
<li>400 Bad Request</li>
<li>401 未授权</li>
<li>403 服务器拒绝请求</li>
<li>404 未找到</li>
<li>409 请求发生冲突</li>
<li>500 服务器内部错误</li>
<li>502 错误网关</li>
<li>503 服务不可用</li>
</ul>
<h2 id="URL-输入到页面加载的过程"><a href="#URL-输入到页面加载的过程" class="headerlink" title="URL 输入到页面加载的过程"></a>URL 输入到页面加载的过程</h2><ol>
<li>用户输入 URL，浏览器进程通过 URL 和协议组装为完整 URL。</li>
<li>网络进程接收到浏览器进程通过进程通信发送的 URL 后检查本地缓存资源。</li>
<li>网络进程没有请求缓存时向服务器发起 HTTP 请求：通过 DNS 查询获取服务器的 IP 地址和端口、通过 IP 和服务器建立 TCP 连接、发送请求并接收服务器的响应内容。</li>
<li>网络进程解析响应内容：状态码为 301&#x2F;302 时重定向，200 时则检查响应类型 Content-Type，字节流时将请求转给下载管理器并不再渲染，html 则发给渲染进程处理。</li>
<li>渲染进程构建 DOM 树：如果 URL 和已打开的渲染进程根域名不同时会开启新进程。</li>
<li>浏览器进程根据 DOM 树更新页面：关闭连接并重复请求至资源全部加载后更新页面。</li>
</ol>
<h2 id="HTTP-请求流程"><a href="#HTTP-请求流程" class="headerlink" title="HTTP 请求流程"></a>HTTP 请求流程</h2><ol>
<li>构建请求：浏览器构建请求行后准备发起网络请求。</li>
<li>查找缓存：浏览器首先会查询缓存中是否有请求资源副本，有则拦截请求并返回资源副本。</li>
<li>准备 IP 地址和端口：浏览器请求 DNS 返回域名对应 IP 并缓存域名解析结果。</li>
<li>等待 TCP 队列：Chrome 机制下同一个域名同时最多只能建立 6 个 TCP 连接。</li>
<li>建立 TCP 连接：TCP 通过三次握手建立连接和传输数据，通过四次挥手断开连接。</li>
<li>发送 HTTP 请求：浏览器建立 TCP 连接后首先会向服务器发送请求行，然后以请求头形式发送一些其他信息，如果是 POST 请求还会发送请求体。</li>
<li>服务器处理请求：如果请求头或响应头有 Connection:keep-alive TCP 则保持打开状态。</li>
<li>服务器返回请求和断开连接。</li>
</ol>
<h2 id="HTTPS-原理-握手过程"><a href="#HTTPS-原理-握手过程" class="headerlink" title="HTTPS 原理 (握手过程)"></a>HTTPS 原理 (握手过程)</h2><ol>
<li>客户端使用 https 的 url 访问 web 服务器，要求与服务器建立 ssl 连接。</li>
<li>web 服务器收到客户端请求后，会将网站的证书（包含公钥）传送一份给客户端。</li>
<li>客户端收到网站证书后会检查颁发机构和过期时间，没问题则随机生成一个秘钥。</li>
<li>客户端利用公钥将会话秘钥加密，并传送给服务端，服务端利用自己的私钥解密。</li>
<li>之后服务器与客户端使用秘钥加密传输。</li>
</ol>
<h2 id="HTTPS-握手时客户端验证证书"><a href="#HTTPS-握手时客户端验证证书" class="headerlink" title="HTTPS 握手时客户端验证证书"></a>HTTPS 握手时客户端验证证书</h2><ol>
<li>首先浏览器读取证书中的证书所有者、有效期等信息进行校验，校验证书的网站域名是否与证书颁发的域名一致，校验证书是否在有效期内。</li>
<li>浏览器开始查找操作系统中已内置的受新人的证书发布机构 CA，与服务器发来的证书中的颁发者 CA 比对，用于校验证书是否为合法机构颁发。</li>
<li>如果找不到，浏览器就会报错，说明浏览器发来的证书是不可信任的。</li>
<li>如果找到，浏览器就会从操作系统中取出颁发者 CA 的公钥（浏览器开发商一般会实现在内部植入常用认证机关的公开密钥），然后对服务器发来的证书里面的签名进行解密。</li>
<li>浏览器使用相同 hash 算法计算出服务器发送证书的 hash 值，然后与证书中签名做对比。</li>
<li>对比结果一致，则证明服务器发来的证书合法，没有被冒充。</li>
</ol>
<h2 id="HTTP2-多路复用"><a href="#HTTP2-多路复用" class="headerlink" title="HTTP2 多路复用"></a>HTTP2 多路复用</h2><ul>
<li>HTTP&#x2F;1 每次请求都会建立一次 HTTP 连接，即 3 次握手和 4 次挥手，这个过程在一次请求过程中占用了较长时间，即使开启了 Keep-Alive 解决了多次连接的问题，但是依然有两个效率上的问题，一是串行的文件传输，二是连接数过多导致的性能问题。HTTP&#x2F;2 的多路复用就是为了解决上述的两个性能问题。</li>
<li>HTTP&#x2F;2 有两个重要概念，分别是帧（frame）和流（stream）。帧代表着最小的数据单位，每个帧会标识出该帧属于哪个流，流是多个帧组成的数据流。</li>
<li>多路复用，就是在一个 TCP 连接中可以存在多条流。即可以发送多个请求，对端可以通过帧中的标识知道属于哪个请求。通过这个技术，可以避免 HTTP 旧版本中的队头阻塞问题，极大的提高传输性能。</li>
</ul>
<h2 id="HTTPS-中间人攻击"><a href="#HTTPS-中间人攻击" class="headerlink" title="HTTPS 中间人攻击"></a>HTTPS 中间人攻击</h2><blockquote>
<p>https 协议由 http + ssl 协议构成。攻击的防范方法：服务器在发送浏览器的公钥中加入 CA 证书，浏览器可以验证 CA 证书的有效性（HTTPS 只有信任了劫持者的 CA 证书才会被劫持）。中间人攻击过程如下：</p>
</blockquote>
<ol>
<li>服务器向客户端发送公钥。</li>
<li>攻击者截获公钥，保留在自己手上。</li>
<li>然后攻击者自己生成一个【伪造的】公钥，发给客户端。</li>
<li>客户端收到伪造的公钥后，生成加密 hash（秘钥） 值发给服务器。</li>
<li>攻击者获得加密 hash 值，用自己的私钥解密获得真秘钥。</li>
<li>同时生成假的加密 hash 值，发给服务器。</li>
<li>服务器用私钥解密获得假秘钥。</li>
<li>服务器用假秘钥加密传输信息。</li>
</ol>
<h2 id="SSL-连接断开后恢复"><a href="#SSL-连接断开后恢复" class="headerlink" title="SSL 连接断开后恢复"></a>SSL 连接断开后恢复</h2><ul>
<li><code>Session ID</code>：每次会话都有一个编号，当对话中断后重新连接时，只要客户端提供编号并且服务器有该编号的记录，则双方可以继续使用以前的密钥，而不用重新生成。</li>
<li><code>Session Ticket</code>：服务器在上次对话中发送给客户的。ticket 是加密的并且只有服务器能够解密，里面包含了对话密钥和加密方法等会话信息。不管请求是否转移到其他服务器，服务器将 ticket 解密后就能获取上次对话的信息而不用重新生成对话秘钥了。</li>
</ul>
<h2 id="前端鉴权方案"><a href="#前端鉴权方案" class="headerlink" title="前端鉴权方案"></a>前端鉴权方案</h2><blockquote>
<p>HTTP-Basic-Authentication 适用于内部网络或对安全要求不高的网络，Session-Cookie 适用于一般中大型的网站，Token JWT 适用于大多数企业型网站(后者性能更好)，SSO 单点登录适用于子系统较多的大型企业网站，OAuth 授权适用于需要快速注册用户型的网站，扫码登录 适用于已完成部署了三端的企业，一键登录 适用于原生 APP。</p>
</blockquote>
<ul>
<li><strong>Identification 认证</strong>：根据声明者所特有的识别信息来确认身份，比如身份证、电子邮箱、用户名密码、手机短信&#x2F;二维码&#x2F;手势、指纹&#x2F;语音等生物学特征、大数据识别。</li>
<li><strong>Authorization 授权</strong>：指资源所有者赋予执行者的操作权限。生活领域比如银行的银行卡、物业的门禁卡，互联网领域比如 服务器 session、浏览器 cookie、授权令牌 token。</li>
<li><strong>Authentication 鉴权</strong>：对声明者身份的真实性进行鉴别确认。比如门禁卡的识别器。</li>
<li><strong>Permission 权限控制</strong>：通过权限列表判断操作是否允许。比如不同用户可访问的菜单。</li>
<li>鉴权方案如下：以上四个环节是依次发生的上下游关系，比如门禁卡开门时四个环节同时发生、用户登录时只完成认证和授权而在选择限购商品时才会进行身份鉴权和权限控制。</li>
<li><code>HTTP-Basic-Authentication</code>：通过 base64 加密后的用户名和密码实现身份验证。</li>
<li><code>Session-Cookie</code>：服务器在浏览器首次访问时创建唯一标识 sessionId、浏览器解析响应头后将标示保存到 Cookie 并在下次请求时添加到请求头、服务器通过对比 sessionId 来判断合法性。缺点在于 Cookie 容易 CSRF 攻击、大量用户的 Session 会降低服务器性能。</li>
<li><code>Token</code>：访问资源接口时所需要的凭证。服务器在浏览器首次访问(比如登录)时验证通过后会签发一张令牌，浏览器每次请求时都携带令牌访问，服务端只需验证令牌的有效性即可。Token 组成一般为 uid (用户身份的唯一标识) + time (当前时间的时间戳) + sign (签名：前几位以哈希算法压缩成的十六进制字符串)，注意 Token 可设置有效期并且不存到服务器。</li>
<li><code>JWT (JSON-Web-Token)</code>：服务端验证客户端发送的 Token 时还需要查询数据库获取用户信息来验证，Auth0 就提出了通过包含用户信息的 JSON 对象进行加密签名来实现授权验证的方案。JWT 三个以点分割的组成部分为 Header 头部、Payload 负载、Signature 签名，加密过程为服务器通过随机数和加密算法对 Payload 信息生成一个字符串 Token。</li>
<li><code>SSO (Single-Sign-On) 单点登录</code>：允许用户通过一次登录即可访问不同应用程序的认证系统，比如支付宝和淘宝。同域下的子系统相互访问时需要跳转到 SSO 认证中心提供的登录页面，比如百度贴吧和网盘 tieba.baidu.com pan.baidu.com。跨域下的子系统则需要通过中央授权服务 CAS，比如天猫和淘宝 tmall.com taobao.com。</li>
<li><code>OAuth2.0 (Open-Authority) 开放授权</code>：一种基于回调的授权协议，允许用户授权第三方应用访问他在其它服务器上的信息而不需要提供用户名和密码，客户端通过授权码、隐藏式、密码式、客户端凭证的四种授权模式获取短期令牌或授权码来访问第三方网站。</li>
<li><code>联合登录和信任登录</code>：联合登录指使用第三方凭证进行校验的登录服务，常见的身份担保平台有微信和支付宝，常见场景比如 APP 内嵌 H5，只有应用已登录的用户才能访问 H5。信任登录指所有不需要用户主动参与的登录，比如用户与私有设备绑定后即可通过私有设备的信息作为凭证，常见的第三方信任登录帐号比如支付宝帐号和微博帐号。</li>
<li><code>唯一登录</code>：禁止多人同时登录同一账号，后登录行为会导致前者掉线。服务端在用户登录时生成 Token 并保存一个登录状态，新用户登录时会验证登录状态后更新 Token。</li>
<li><code>扫码登录</code>：手机客户端扫描二维码后会将登录凭证 Token 和自动获取的二维码 ID 作为参数发送给服务端、服务端将收到的 Token ID 建立关联用于手机退出登录时同步，然后生成一个临时 Token 返回给手机端用作确认时的凭证，并在用户点击确认按钮后更新二维码状态并向 PC 端发送正式 Token，PC 端轮询到二维码已登录状态后通过 Token 完成登录。</li>
<li><code>一键登录</code>：通过手机号码一键授权来完成注册登录功能，不需要填写验证码和繁琐的个人信息即可快速完成账号的注册登录。该功能需要运营商开放相关服务，但是移动等三大运营商 SDK 兼容工作比较繁琐而建议采用闪验、秒验等第三方号码认证服务。</li>
</ul>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br></pre></td><td class="code"><pre><span class="line">response_type：code 返回授权码，token 返回令牌</span><br><span class="line">client_id：客户端身份标识</span><br><span class="line">client_secret：客户端密钥</span><br><span class="line">redirect_uri：重定向地址</span><br><span class="line">scope：表示授权范围，read 只读权限，all 读写权限</span><br><span class="line">grant_type：表示授权方式，<span class="variable constant_">AUTHORIZATION_CODE</span> 授权码、password 密码、</span><br><span class="line">client_credentials 凭证式 、refresh_token 更新令牌</span><br><span class="line">state：应用程序传递的一个随机数，用来防止<span class="variable constant_">CSRF</span>攻击。</span><br><span class="line"></span><br><span class="line"><span class="comment">// 授权码</span></span><br><span class="line"><span class="comment">// 1、通过微信账号登录掘金时，掘金发起授权请求</span></span><br><span class="line"><span class="attr">https</span>:<span class="comment">//wx.com/oauth/authorize? </span></span><br><span class="line">response_type=code&amp; </span><br><span class="line">client_id=<span class="variable constant_">CLIENT_ID</span>&amp; </span><br><span class="line">redirect_uri=<span class="attr">http</span>:<span class="comment">//juejin.im/callback&amp;</span></span><br><span class="line">scope=read</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2、用户同意授权后，微信通过 redirect_uri 重定向并带上授权码</span></span><br><span class="line"><span class="attr">http</span>:<span class="comment">//juejin.im/callback?code=AUTHORIZATION_CODE</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3、掘金拿到授权码时，带授权码和密匙等参数向微信申请令牌</span></span><br><span class="line"><span class="attr">https</span>:<span class="comment">//wx.com/oauth/token?</span></span><br><span class="line">client_id=<span class="variable constant_">CLIENT_ID</span>&amp;</span><br><span class="line">client_secret=<span class="variable constant_">CLIENT_SECRET</span>&amp;</span><br><span class="line">grant_type=authorization_code&amp;</span><br><span class="line">code=<span class="variable constant_">AUTHORIZATION_CODE</span>&amp;</span><br><span class="line">redirect_uri=<span class="attr">http</span>:<span class="comment">//juejin.im/callback</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 4、微信收到请求后向 redirect_uri 地址发送 JSON</span></span><br><span class="line">&#123;    </span><br><span class="line">    <span class="string">&quot;access_token&quot;</span>: <span class="string">&quot;ACCESS_TOKEN&quot;</span>,</span><br><span class="line">    <span class="string">&quot;token_type&quot;</span>: <span class="string">&quot;bearer&quot;</span>,</span><br><span class="line">    <span class="string">&quot;expires_in&quot;</span>: <span class="number">2592000</span>,</span><br><span class="line">    <span class="string">&quot;refresh_token&quot;</span>: <span class="string">&quot;REFRESH_TOKEN&quot;</span>,</span><br><span class="line">    <span class="string">&quot;scope&quot;</span>:<span class="string">&quot;read&quot;</span>,</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 隐藏式：对于纯前端应用跳过授权码，授权通过后重定向到 redirect_uri</span></span><br><span class="line"><span class="attr">https</span>:<span class="comment">//wx.com/oauth/authorize?</span></span><br><span class="line">response_type=token&amp;</span><br><span class="line">client_id=<span class="variable constant_">CLIENT_ID</span>&amp;</span><br><span class="line">redirect_uri=<span class="attr">http</span>:<span class="comment">//juejin.im/callback&amp;</span></span><br><span class="line">scope=read</span><br><span class="line"></span><br><span class="line"><span class="comment">// 密码式：掘金通过用户名和密码直接去微信申请令牌，只适用于信任应用</span></span><br><span class="line"><span class="attr">https</span>:<span class="comment">//wx.com/token?</span></span><br><span class="line">grant_type=password&amp;</span><br><span class="line">username=<span class="variable constant_">USERNAME</span>&amp;</span><br><span class="line">password=<span class="variable constant_">PASSWORD</span>&amp;</span><br><span class="line">client_id=<span class="variable constant_">CLIENT_ID</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 凭证式：适用于那些没有前端的命令行应用，可以用最简单的方式获取令牌</span></span><br><span class="line"><span class="attr">https</span>:<span class="comment">//wx.com/token?</span></span><br><span class="line">grant_type=client_credentials&amp;</span><br><span class="line">client_id=<span class="variable constant_">CLIENT_ID</span>&amp;</span><br><span class="line">client_secret=<span class="variable constant_">CLIENT_SECRET</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 令牌过期时请求更新</span></span><br><span class="line"><span class="comment">// https://wx.com/oauth/token?</span></span><br><span class="line">grant_type=refresh_token&amp;</span><br><span class="line">client_id=<span class="variable constant_">CLIENT_ID</span>&amp;</span><br><span class="line">client_secret=<span class="variable constant_">CLIENT_SECRET</span>&amp;</span><br><span class="line">refresh_token=<span class="variable constant_">REFRESH_TOKEN</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> App 授权 QQ/微信/微博登录</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@prame</span> 1、打开 manifest.json -&gt; App 模块权限配置勾选 OAuth。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@prame</span> 2、打开 manifest.json -&gt; App SDK 配置 --&gt; 登陆鉴权。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@prame</span> 3、向微信等平台申请到链接 sdk 的信息后，回填到 manifest。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@prame</span> 4、这些配置需要打包生效，真机运行仍然是 HBuilder 基座的设置，</span></span><br><span class="line"><span class="comment"> * 可使用自定义基座包。离线打包请参考离线打包文档在原生工程中配置。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@prame</span> 5、配置并打包后，通过 uni.getProvider 可以得到配置的结果列表，</span></span><br><span class="line"><span class="comment"> * 注意这里返回的是 manifest 配置的，与手机端是否安装微信、QQ、微博无关。</span></span><br><span class="line"><span class="comment"> **/</span> </span><br><span class="line"><span class="title function_">appOAuthLogin</span>(<span class="params">e</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> me = <span class="variable language_">this</span>;</span><br><span class="line">    <span class="comment">// 1 获取用户的登录类型</span></span><br><span class="line">    <span class="keyword">var</span> logintype = e.<span class="property">currentTarget</span>.<span class="property">dataset</span>.<span class="property">logintype</span>;</span><br><span class="line">    <span class="comment">// 2 授权登录，弹出授权窗口</span></span><br><span class="line">    uni.<span class="title function_">login</span>(&#123;</span><br><span class="line">        <span class="attr">provider</span>: logintype,</span><br><span class="line">        <span class="title function_">success</span>(<span class="params">loginRes</span>) &#123;</span><br><span class="line">          <span class="comment">// 3 授权登录成功以后，获取用户的信息</span></span><br><span class="line">          uni.<span class="title function_">getUserInfo</span>(&#123;</span><br><span class="line">            <span class="attr">provider</span>: logintype,</span><br><span class="line">            <span class="title function_">success</span>(<span class="params">info</span>) &#123;</span><br><span class="line">              <span class="keyword">var</span> userInfo = info.<span class="property">userInfo</span>;</span><br><span class="line">              <span class="keyword">var</span> face = <span class="string">&quot;&quot;</span>;</span><br><span class="line">              <span class="keyword">var</span> nickname = <span class="string">&quot;&quot;</span>;</span><br><span class="line">              <span class="keyword">var</span> openIdOrUid = <span class="string">&quot;&quot;</span>;</span><br><span class="line">              <span class="keyword">if</span> (logintype == <span class="string">&quot;weixin&quot;</span>) &#123;</span><br><span class="line">                  openIdOrUid = userInfo.<span class="property">openId</span>;</span><br><span class="line">                  face = userInfo.<span class="property">avatarUrl</span>;</span><br><span class="line">                  nickname = userInfo.<span class="property">nickName</span>;</span><br><span class="line">              &#125; <span class="keyword">else</span> <span class="keyword">if</span> (logintype == <span class="string">&quot;qq&quot;</span>) &#123;</span><br><span class="line">                  openIdOrUid = userInfo.<span class="property">openId</span>;</span><br><span class="line">                  face = userInfo.<span class="property">figureurl_qq_2</span>;</span><br><span class="line">                  nickname = userInfo.<span class="property">nickname</span>;</span><br><span class="line">              &#125; <span class="keyword">else</span> <span class="keyword">if</span> (logintype == <span class="string">&quot;sinaweibo&quot;</span>) &#123;</span><br><span class="line">                  openIdOrUid = userInfo.<span class="property">id</span>;</span><br><span class="line">                  face = userInfo.<span class="property">avatar_large</span>;</span><br><span class="line">                  nickname = userInfo.<span class="property">nickname</span>;</span><br><span class="line">              &#125;</span><br><span class="line">              <span class="comment">// 4 调用开发者后台，执行一键注册或登录</span></span><br><span class="line">              uni.<span class="title function_">request</span>(&#123;</span><br><span class="line">                  <span class="attr">url</span>: me.<span class="property">serverUrl</span> + <span class="string">&quot;/appUnionLogin/&quot;</span> + logintype,</span><br><span class="line">                  <span class="attr">data</span>: &#123;</span><br><span class="line">                      <span class="string">&quot;openIdOrUid&quot;</span>: openIdOrUid,</span><br><span class="line">                      <span class="string">&quot;nickname&quot;</span>: nickname,</span><br><span class="line">                      <span class="string">&quot;face&quot;</span>: face</span><br><span class="line">                  &#125;,</span><br><span class="line">                  <span class="attr">method</span>: <span class="string">&quot;POST&quot;</span>,</span><br><span class="line">                  <span class="title function_">success</span>(<span class="params">result</span>) &#123;</span><br><span class="line">                      <span class="keyword">if</span> (result.<span class="property">data</span>.<span class="property">status</span> == <span class="number">200</span>) &#123;</span><br><span class="line">                          <span class="keyword">var</span> userInfo = result.<span class="property">data</span>.<span class="property">data</span>;</span><br><span class="line">                          <span class="comment">// 5 保存用户信息到全局的缓存中</span></span><br><span class="line">                          uni.<span class="title function_">setStorageSync</span>(<span class="string">&quot;globalUser&quot;</span>, userInfo);</span><br><span class="line">                          <span class="comment">// 6 切换页面跳转，使用 tab 切换的 api</span></span><br><span class="line">                          uni.<span class="title function_">switchTab</span>(&#123; <span class="attr">url</span>: <span class="string">&quot;../me/me&quot;</span> &#125;);</span><br><span class="line">                      &#125;</span><br><span class="line">                  &#125;</span><br><span class="line">              &#125;)</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 一键登录步骤</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@prame</span> 1、SDK 初始化：调用 SDK 传入平台配置的 AppKey、AppSecret</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@prame</span> 2、唤起授权页：调用 SDK 唤起授权接口，SDK 会先向运营商发起获取</span></span><br><span class="line"><span class="comment"> 手机号掩码的请求，请求成功后跳到授权页显示手机号掩码和运营商协议给用户确认。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@prame</span> 3、同意授权并登录：SDK 在用户同意协议并点击登录后请求 Token</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@prame</span> 4、取号：服务端携带 Token 调用运营商一键登录的接口获取手机号</span></span><br><span class="line"><span class="comment">**/</span></span><br></pre></td></tr></table></figure>


<h1 id="三、设计模式"><a href="#三、设计模式" class="headerlink" title="三、设计模式"></a>三、设计模式</h1><h2 id="设计模式的原则"><a href="#设计模式的原则" class="headerlink" title="设计模式的原则"></a>设计模式的原则</h2><blockquote>
<p>设计模式描述了一个不断发生的问题和解决方案的核心，可以重复使用而减少工作量。解决复杂问题时主要通过分解(将复杂问题简单化)、抽象(忽略细节而处理抽象模型)。</p>
</blockquote>
<ul>
<li><strong>组件协作</strong><ol>
<li>Template Method 模式：定义一个操作中算法的骨架(稳定)，将一些步骤(变化)延迟到子类，比如 React 生命周期。</li>
<li>Observer 模式：定义对象间的一种一对多的依赖关系，当对象 Subject 的状态发生变化时则所有依赖它的对象都得到通知并自动更新，比如 Redux 实现。</li>
<li>Strategy 模式：定义一系列算法并封装，并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展、子类化)，比如国际化语言。</li>
</ol>
</li>
<li><strong>单一职责</strong><ol>
<li>Decorator 模式：给一个对象动态(组合)增加额外功能，在增加功能方面比生成子类(继承)更加灵活，比如 React 高阶组件、中间件。</li>
<li>Bridge 模式：将业务功能与平台实现分离，使它们都可以独立变化，比如组件。</li>
</ol>
</li>
<li><strong>对象创建</strong><ol>
<li>Factory Method 模式：定义一个用于创建对象的接口，让子类决定实例化哪一个类。Factory Method 使得一个类的实例化延迟（目的：解耦，手段：抽象）到子类，比如 React.createElement()、gin.Default()。</li>
<li>Abstract Factory：提供一个接口，让该接口负责创建一系列相关或相互依赖的对象而无需指定他们具体的类，比如 SQL API 有连接类、创建类、操作类。</li>
</ol>
</li>
<li><strong>对象性能</strong><ol>
<li>Singleton 模式：一个类只有一个实例并提供实例的全局访问点，比如游戏主角。</li>
<li>FlyWeight 享元模式：运行共享技术有效支持大量细粒度的对象，比如对象池优化。</li>
</ol>
</li>
<li><strong>接口隔离</strong><ol>
<li>Facade 模式：为子系统的一组接口提供一致(稳定)的界面，Facade 模式定义了一个方便子系统复用的高层接口，比如各种硬件驱动。</li>
<li>Proxy 模式：为其他对象提供一种代理以控制(隔离、使用接口)对这种对象的访问，比如使用闭包导出方法(代理操作对象)。</li>
<li>Adapter 模式：将一个类的接口转换为客户希望的另一个接口，使得原本由于接口不兼容而不能一起工作的那些类可以一起工作，比如 Go http.ListenAndServe 所接受的结构体只需要实现 ServeHTTP 方法即可满足 http 的接口条件。</li>
<li>Composite 模式：将对象组合成树形结构以表示 部分-整体 的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性(稳定)，比如树形结构中 Parent&#x2F;Child Node 都继承于同一抽象类 Node 并通过 process 执行方法。</li>
</ol>
</li>
</ul>
<h2 id="观察者模式、订阅-发布模式"><a href="#观察者模式、订阅-发布模式" class="headerlink" title="观察者模式、订阅-发布模式"></a>观察者模式、订阅-发布模式</h2><blockquote>
<p>发布-订阅模式是观察者模式的一种变体，它只是把一部分功能抽象成一个独立的变更管理器。一对多时使用观察者，多对多时则使用订阅-发布。观察者模式的主体和观察者是互相感知的，发布-订阅模式是借助第三方来实现调度的，发布者和订阅者之间互不感知。</p>
</blockquote>
<h2 id="面向对象的设计原则"><a href="#面向对象的设计原则" class="headerlink" title="面向对象的设计原则"></a>面向对象的设计原则</h2><ul>
<li><strong>依赖倒置原则</strong>：高层模块(稳定)不应该依赖低层模块(变化)，两者都应该依赖于抽象(稳定)。抽象(稳定)不应该依赖于实现细节(变化)，实现细节(变化)应该依赖于抽象(稳定)。</li>
<li><strong>开放封闭原则</strong>：对扩展开放而对更改封闭，类模块应该是可扩展的但不可修改。</li>
<li><strong>单一职责原则</strong>：一个类应该只有一个引起它变化的原因，变化方向隐含了类的责任。</li>
<li><strong>Liskov 替换原则</strong>：子类必须能够替换他们的基类 IS-A，继承表达类型抽象。</li>
<li><strong>接口隔离原则</strong>：不应该强迫客户端使用他们不用的方法，接口应该小而完备。</li>
<li><strong>优先使用对象组合，而不是类继承</strong>：类继承通常为白箱复用，对象组合通常为黑箱复用。继承在某种程度上破坏了封装性而导致子类和父类耦合度高，对象组合则只要求被组合的对象具有良好定义的接口而耦合度低。&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D; 白箱复用为 B 类继承 A 类的功能并了解内部细节从而实现复用但是耦合性较强，黑箱复用则是 B 类只需要关注 A 类所暴露的一些外部方法即可实现复用从而达到了解耦效果。</li>
<li><strong>封装变化点</strong>：使用封装来创建对象之间的分界层，让设计者可以在分界层的一侧进行修改，而不会对另一侧产生不良的影响，从而实现层次间的松耦合。</li>
<li><strong>针对接口编程，而不是针对实现编程</strong>：不将变量类型声明为具体的某个类，而是声明为某个接口。客户程序无需获取对象的具体类型，只需要知道对象所具有的接口。减少系统中的各部分依赖关系，从而实现高内聚、低耦合的类型设计方案。</li>
</ul>
<h2 id="设计一个环境"><a href="#设计一个环境" class="headerlink" title="设计一个环境"></a>设计一个环境</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 设计一个不能操作 DOM 和调接口的环境。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 1、iframe 创建沙箱并将其全局对象赋值为原生浏览器全局对象。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 2、设置一个黑名单，访问名单变量时直接报错而实现隔离效果。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 3、黑名单中添加 document 字段来实现禁止开发者操作 DOM。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 4、黑名单中添加 XMLHttpRequest、fetch、WebSocket 禁原生方式调接口。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 5、访问当前全局对象不存在的变量时直接报错，实现禁用axios等三方库调接口。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 6、拦截对 window 对象的访问防止 window.document 操作 DOM。</span></span><br><span class="line"><span class="comment">**/</span> </span><br><span class="line"><span class="comment">// 沙箱全局代理对象类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SandboxGlobalProxy</span> &#123;</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">blacklist</span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> iframe = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;iframe&quot;</span>, &#123; </span><br><span class="line">            <span class="attr">url</span>: <span class="string">&quot;about:blank&quot;</span> </span><br><span class="line">        &#125;);</span><br><span class="line">        iframe.<span class="property">style</span>.<span class="property">display</span> = <span class="string">&quot;none&quot;</span>;</span><br><span class="line">        <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(iframe);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">const</span> sandboxGlobal = iframe.<span class="property">contentWindow</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Proxy</span>(sandboxGlobal, &#123;</span><br><span class="line">            <span class="comment">// has 可以拦截 with 代码块中任意属性的访问</span></span><br><span class="line">            <span class="attr">has</span>: <span class="function">(<span class="params">target, prop</span>) =&gt;</span> &#123;</span><br><span class="line">                <span class="comment">// 黑名单中的变量禁止访问</span></span><br><span class="line">                <span class="keyword">if</span> (blacklist.<span class="title function_">includes</span>(prop)) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`Can&#x27;t use: <span class="subst">$&#123;prop&#125;</span>!`</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 对象上不存在的属性，直接报错，实现禁用三方库调接口</span></span><br><span class="line">                <span class="keyword">if</span> (!target.<span class="title function_">hasOwnProperty</span>(prop)) &#123;</span><br><span class="line">                  <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`Not find: <span class="subst">$&#123;prop&#125;</span>!`</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 返回 true，获取当前提供上下文对象中的变量</span></span><br><span class="line">                <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// with 关键字：改变代码的作用域，可将对象添加到作用域链的顶部</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">withedYourCode</span>(<span class="params">code</span>) &#123;</span><br><span class="line">    code = <span class="string">&quot;with(sandbox) &#123;&quot;</span> + code + <span class="string">&quot;&#125;&quot;</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Function</span>(<span class="string">&quot;sandbox&quot;</span>, code);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 待执行代码、黑名单、沙箱全局对象、将沙箱对象添加到待执行代码作用域的顶部</span></span><br><span class="line"><span class="keyword">const</span> code = <span class="string">`console.log(document)`</span>;</span><br><span class="line"><span class="keyword">const</span> blacklist = [<span class="string">&#x27;window&#x27;</span>, <span class="string">&#x27;document&#x27;</span>, <span class="string">&#x27;XMLHttpRequest&#x27;</span>, </span><br><span class="line"><span class="string">&#x27;fetch&#x27;</span>, <span class="string">&#x27;WebSocket&#x27;</span>, <span class="string">&#x27;Image&#x27;</span>];</span><br><span class="line"><span class="keyword">const</span> globalProxy = <span class="keyword">new</span> <span class="title class_">SandboxGlobalProxy</span>(blacklist);</span><br><span class="line"><span class="title function_">withedYourCode</span>(code).<span class="title function_">call</span>(globalProxy, globalProxy);</span><br></pre></td></tr></table></figure>


<h1 id="四、算法相关"><a href="#四、算法相关" class="headerlink" title="四、算法相关"></a>四、算法相关</h1><h2 id="迭代方式实现-flatten-函数"><a href="#迭代方式实现-flatten-函数" class="headerlink" title="迭代方式实现 flatten 函数"></a>迭代方式实现 flatten 函数</h2><blockquote>
<p>迭代指按顺序访问数组、对象等的每一项 (查找、遍历)。</p>
</blockquote>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">flatten</span>(<span class="params">arr</span>) &#123;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; arr.<span class="property">length</span>; i++) &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="title class_">Array</span>.<span class="title function_">isArray</span>(arr[i])) &#123;</span><br><span class="line">      arr = arr.<span class="title function_">concat</span>(arr[i])</span><br><span class="line">      arr.<span class="title function_">splice</span>(i, <span class="number">1</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> arr;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> arr = [<span class="number">1</span>, <span class="number">2</span>, [<span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, [<span class="number">6</span>, <span class="number">7</span>], <span class="number">8</span>], <span class="number">9</span>, <span class="number">10</span>, [<span class="number">11</span>, [<span class="number">12</span>, <span class="number">13</span>]]]</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">flatten</span>(arr))</span><br></pre></td></tr></table></figure>

<h2 id="数组的随机排序"><a href="#数组的随机排序" class="headerlink" title="数组的随机排序"></a>数组的随机排序</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 随机数排序</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">random1</span>(<span class="params">arr</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> arr.<span class="title function_">sort</span>(<span class="function">() =&gt;</span> <span class="title class_">Math</span>.<span class="title function_">random</span>() - <span class="number">.5</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 随机插入排序</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">random2</span>(<span class="params">arr</span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> cArr = [...arr];</span><br><span class="line">  <span class="keyword">const</span> newArr = [];</span><br><span class="line">  <span class="keyword">while</span> (cArr.<span class="property">length</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> index = <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * cArr.<span class="property">length</span>);</span><br><span class="line">    newArr.<span class="title function_">push</span>(cArr[index]);</span><br><span class="line">    cArr.<span class="title function_">splice</span>(index, <span class="number">1</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> newArr;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 洗牌算法，随机交换排序</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">random3</span>(<span class="params">arr</span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> l = arr.<span class="property">length</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; l; i++) &#123;</span><br><span class="line">    <span class="keyword">const</span> index = <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * (l - i)) + i;</span><br><span class="line">    <span class="keyword">const</span> temp = arr[index];</span><br><span class="line">    arr[index] = arr[i];</span><br><span class="line">    arr[i] = temp;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> arr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<h1 id="五、Nodejs"><a href="#五、Nodejs" class="headerlink" title="五、Nodejs"></a>五、Nodejs</h1><h2 id="模块"><a href="#模块" class="headerlink" title="模块"></a>模块</h2><blockquote>
<p>Node 中的每个文件模块都是一个对象，定义如下：</p>
</blockquote>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Module</span>(<span class="params">id, parent</span>) &#123;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">id</span> = id;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">exports</span> = &#123;&#125;;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">parent</span> = parent;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">filename</span> = <span class="literal">null</span>;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">loaded</span> = <span class="literal">false</span>;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">children</span> = [];</span><br><span class="line">&#125;</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">Module</span>;</span><br><span class="line"><span class="keyword">var</span> <span class="variable language_">module</span> = <span class="keyword">new</span> <span class="title class_">Module</span>(filename, parent);</span><br></pre></td></tr></table></figure>


<h2 id="require-模块加载机制"><a href="#require-模块加载机制" class="headerlink" title="require 模块加载机制"></a>require 模块加载机制</h2><ol>
<li>计算模块绝对路径。</li>
<li>如果缓存中有该模块，则从缓存中取出该模块。</li>
<li>按优先级依次寻找并编译执行模块，将模块推入缓存（require.cache）。</li>
<li>输出模块的 exports 属性。</li>
</ol>
<h2 id="两个模块互相引用"><a href="#两个模块互相引用" class="headerlink" title="两个模块互相引用"></a>两个模块互相引用</h2><blockquote>
<p>假设 A、B 模块互相引用，此时运行 A 模块的话，先运行的 A 模块将会被缓存，但是此时缓存的是一个未执行完毕的 A 模块，而 A 模块中引入的 B 模块将会被完整加载并且正常使用，而 B 模块中调用的 A 模块将会是个默认的空对象（module.exports 的默认值），不具备 A 模块的任何功能。</p>
</blockquote>
<h2 id="异步-I-x2F-O"><a href="#异步-I-x2F-O" class="headerlink" title="异步 I&#x2F;O"></a>异步 I&#x2F;O</h2><ul>
<li>进程启动时，Node 会创建一个类似于 while(true) 的循环，每执行一次循环体的过程成为 Tick。每个 Tick 的过程就是查看是否有事件待处理，如果有就取出事件及其相关的回调函数。如果存在关联的回调函数就执行，然后进入下个循环，不再有事件处理时退出进程。（每个 Tick 都会把观察者中可执行的事件执行完毕后，再进行下一个 Tick）。</li>
<li>事件循环是一个典型的生产者&#x2F;消费者模型。异步I&#x2F;O、网络请求等则是事件的生产者，生产出的事件被传递到对应的观察者，事件循环从观察者取出事件并处理（消费者）。</li>
<li>具体流程<ul>
<li>1、发起异步调用</li>
<li>2、封装异步执行对象，设置回调函数和参数</li>
<li>3、将异步执行对象推入线程池（主线程继续往下执行）</li>
<li>4、异步执行对象执行完毕后，将执行结果连同线程一起交还给主线程。</li>
<li>5、主线程的 Tick 检测到有执行完成的异步任务，取出对象并执行对应的回调函数。</li>
<li>6、完成</li>
</ul>
</li>
</ul>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 简易型 tick</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> events = [];</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">observer</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="built_in">setInterval</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;checking...&#x27;</span>)</span><br><span class="line">    <span class="keyword">if</span> (events.<span class="property">length</span> &gt; <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">const</span> event = events.<span class="title function_">shift</span>();</span><br><span class="line">      <span class="title function_">event</span>();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;, <span class="number">50</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">fn</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;event callback&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">setInterval</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">  events.<span class="title function_">push</span>(fn);</span><br><span class="line">&#125;, <span class="number">500</span>);</span><br><span class="line"></span><br><span class="line"><span class="title function_">observer</span>();</span><br></pre></td></tr></table></figure>


<h2 id="Buffer"><a href="#Buffer" class="headerlink" title="Buffer"></a>Buffer</h2><blockquote>
<p>Node 中用于处理二进制数据的类，其中与 IO 相关的操作（网络&#x2F;文件等）均基于 Buffer。Buffer 类的实例非常类似于整数数组，但其大小是固定不变的，并且其内存在 V8 堆栈外分配原始内存空间。Buffer 类的实例创建之后，其所占用的内存大小就不能再进行调整。</p>
</blockquote>
<h2 id="Stream"><a href="#Stream" class="headerlink" title="Stream"></a>Stream</h2><blockquote>
<p>Node 中处理流式数据的抽象接口，stream 模块用于构建实现了流接口的对象。Node 提供了 http 请求、process.stdout 等多种流对象。流可以是可读的、可写的、或者可读可写的，所有的流都是 EventEmitter 的实例。</p>
</blockquote>
<h2 id="IPC"><a href="#IPC" class="headerlink" title="IPC"></a>IPC</h2><ul>
<li>IPC 又称进程间通信技术，是用于 Node 内部父子进程之间进行通信的方法。</li>
<li>Node IPC 是通过不同平台的管道技术实现的，特点是本地网络通信、速度快、效率高。</li>
<li>Node 在启动子进程时，主进程先建立 IPC 通道，然后将 IPC 通道的 fd（文件描述符）通过环境变量（NODE_CHANNEL_FD）的方式传递给子进程，然后子进程通过 fd 与 父进程建立 IPC 连接。</li>
</ul>
<h2 id="实现热更新"><a href="#实现热更新" class="headerlink" title="实现热更新"></a>实现热更新</h2><blockquote>
<p>Node API require.cache，该对象引用被清除后重新调用时就会重新加载，这个机制可以用来热加载更新的模块。然后使用 fs.watchFile 监听文件的更改，文件更改后调用 clearCache 传入对应的模块名即可。使用 pm2 reload 可以实现暴力热更新，它会保证在新实例重启成功后才会把旧进程杀死，可以保持服务一直在可响应状态。</p>
</blockquote>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">clearCache</span>(<span class="params">modulePath</span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> path = <span class="built_in">require</span>.<span class="title function_">resolve</span>(modulePath);</span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">require</span>.<span class="property">cache</span>[path]) &#123;</span><br><span class="line">    <span class="built_in">require</span>.<span class="property">cache</span>[path] = <span class="literal">null</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="Node-更适合处理-I-x2F-O-密集型任务还是-CPU-密集型任务"><a href="#Node-更适合处理-I-x2F-O-密集型任务还是-CPU-密集型任务" class="headerlink" title="Node 更适合处理 I&#x2F;O 密集型任务还是 CPU 密集型任务"></a>Node 更适合处理 I&#x2F;O 密集型任务还是 CPU 密集型任务</h2><ul>
<li>Node 更适合处理 I&#x2F;O 密集型的任务。因为 Node 的 I&#x2F;O 密集型任务可以异步调用，利用事件循环的处理能力，资源占用极少，并且事件循环能力避开了多线程的调用，在调用方面是单线程，内部处理其实是多线程的。</li>
<li>由于 JS 是单线程，Node 不适合处理 CPU 密集型的任务，CPU 密集型的任务会导致 CPU 时间片不能释放，使得后续 I&#x2F;O 无法发起，从而造成阻塞。但是可以利用到多进程的特点完成对一些 CPU 密集型任务的处理，不过由于 JS 并不支持多线程，所以在这方面的处理能力会弱于其他多线程语言（例如 Java、Go）。</li>
</ul>
<h2 id="Node-为何不给每个-js-文件以独立的上下文来避免作用域被污染"><a href="#Node-为何不给每个-js-文件以独立的上下文来避免作用域被污染" class="headerlink" title="Node 为何不给每个 js 文件以独立的上下文来避免作用域被污染"></a>Node 为何不给每个 js 文件以独立的上下文来避免作用域被污染</h2><blockquote>
<p>Node 模块正常情况下对作用域不会造成污染（模块函数内执行），意外创建全局变量是一种例外，可以采用严格模式来避免。</p>
</blockquote>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>) &#123;</span><br><span class="line">  a = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_">fn</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a); <span class="comment">// 1</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">a</span>); <span class="comment">// undefined</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">global</span>.<span class="property">a</span>); <span class="comment">// 1 意外的全局上下文污染</span></span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(b); <span class="comment">// 10</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">b</span>); <span class="comment">// undefined</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">global</span>.<span class="property">b</span>); <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure>


<h2 id="console-log-是同步还是异步"><a href="#console-log-是同步还是异步" class="headerlink" title="console.log 是同步还是异步?"></a>console.log 是同步还是异步?</h2><blockquote>
<p>内部实现是 process.stdout，将输入内容打印到 stdout，同步异步取决于 stdout 连接的数据流的类型（需要写入的位置）以及不同的操作系统。造成这种差异的原因是因为一些历史遗留问题，不过这个问题并不会影响正常的输出结果。</p>
</blockquote>
<ul>
<li>文件：Windows、POSIX 同步。</li>
<li>TTY（终端）：Windows 异步，POSIX 同步。</li>
<li>管道（和 socket）：Windows 同步，POSIX 异步。</li>
</ul>
<h2 id="父-x2F-子进程的死亡有何影响"><a href="#父-x2F-子进程的死亡有何影响" class="headerlink" title="父&#x2F;子进程的死亡有何影响"></a>父&#x2F;子进程的死亡有何影响</h2><ul>
<li>子进程死亡不会影响父进程，不过子进程死亡时会向它的父进程发送死亡信号。反之父进程死亡，一般情况下子进程也会随之死亡，但如果此时子进程处于可运行状态、僵死状态等等的话，子进程将被 init 进程手痒，从而成为孤儿进程。</li>
<li>子进程死亡时（处于“终止状态”），父进程没有及时调用 wait() 或 waitpid() 来返回死亡进程的相关信息，此时子进程还有一个 PCB 残留在进程表中，被成为僵尸进程。</li>
</ul>
<h2 id="守护进程"><a href="#守护进程" class="headerlink" title="守护进程"></a>守护进程</h2><blockquote>
<p>不依赖终端 (tty) 的进程，不会因为用户退出终端而停止运行的进程。实现思路如下：</p>
</blockquote>
<ol>
<li>创建一个进程 A。</li>
<li>在进程 A 中创建进程 B，可以使用 child_process.fork 或其他方法。</li>
<li>启动子进程时，设置 detached 属性为 true，保证子进程在父进程退出后继续运行。</li>
<li>进程 A 退出，进程 B 由 init 进程接管。此时进程 B 为守护进程。</li>
</ol>
<h2 id="粘包问题"><a href="#粘包问题" class="headerlink" title="粘包问题"></a>粘包问题</h2><blockquote>
<p>默认情况下，TCP 连接会采用延迟传送算法（Nagle 算法），在数据发送之前缓存他们。如果短时间有多个数据发送，会缓冲到一起作一次发送（缓冲大小是 socket.bufferSize），这样可以减少 IO 消耗提高性能。（TCP 有这个问题，HTTP 协议已解决）。解决方法如下：</p>
</blockquote>
<ul>
<li>多次发送之前间隔一个等待时间：处理简单，但是影响传输效率。</li>
<li>关闭 Nagle 算法：消耗资源高，整体性能下降。</li>
<li>封包&#x2F;拆包：使用一些有标识来进行封包拆包（类似 HTTP 协议头尾）。</li>
</ul>
<h2 id="cookie、session"><a href="#cookie、session" class="headerlink" title="cookie、session"></a>cookie、session</h2><ul>
<li>session 存在服务端，cookie 存在客户端。session 比 cookie 更安全，而且 cookie 不一定一直能用（可能被浏览器禁止）。服务端可以通过设 cookie 的值为空并设置一个及时的 expires 来清除存在客户端上的 cookie。</li>
<li>cookie 可能会包含一些关键信息，而 session 一般都是一个加密串。</li>
</ul>
<h2 id="hosts-文件"><a href="#hosts-文件" class="headerlink" title="hosts 文件"></a>hosts 文件</h2><blockquote>
<p>一个没有扩展名的系统文件，作用是将网址域名和对应的 IP 地址建立一个关联“数据库”。当用户在浏览器中输入一个 url 时，系统会先从 hosts 文件中寻找对应的 IP 地址。</p>
</blockquote>

    </div>

    
    
    <div>
      
        <div>
    
        <div style="text-align:center;color: #ccc;font-size:14px;margin-top:20px;">-------------本文结束<i class="fa fa-paw"></i>感谢您的阅读-------------</div>
    
</div>

      
    </div>
    
        

<div>
<ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>chuangges
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="https://chuangges.gitee.io/2020/04/05/13-%E9%9D%A2%E8%AF%95%E9%A2%98%E4%B9%8B%E6%8F%90%E5%8D%87%E7%AF%87/" title="面试题之提升篇">https://chuangges.gitee.io/2020/04/05/13-面试题之提升篇/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/zh_CN" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>


      <footer class="post-footer">

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2020/03/22/12-%E9%9D%A2%E8%AF%95%E9%A2%98%E4%B9%8B%E5%9F%BA%E7%A1%80%E7%AF%87/" rel="prev" title="面试题之基础篇">
      <i class="fa fa-chevron-left"></i> 面试题之基础篇
    </a></div>
      <div class="post-nav-item">
    <a href="/2020/09/09/14-1-angular%E5%9F%BA%E7%A1%80/" rel="next" title="Angular 项目开发">
      Angular 项目开发 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%80%E3%80%81%E6%BA%90%E7%A0%81%E5%AE%9E%E7%8E%B0"><span class="nav-number">1.</span> <span class="nav-text">一、源码实现</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%95%B0%E7%BB%84%E6%89%81%E5%B9%B3%E5%8C%96"><span class="nav-number">1.1.</span> <span class="nav-text">数组扁平化</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%87%BD%E6%95%B0%E6%9F%AF%E9%87%8C%E5%8C%96"><span class="nav-number">1.2.</span> <span class="nav-text">函数柯里化</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85%E6%A8%A1%E5%BC%8F"><span class="nav-number">1.3.</span> <span class="nav-text">发布订阅模式</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%88%97%E8%A1%A8%E5%92%8C%E6%A0%91%E5%BD%A2%E4%BA%92%E8%BD%AC"><span class="nav-number">1.4.</span> <span class="nav-text">列表和树形互转</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#bind%E3%80%81call%E3%80%81apply"><span class="nav-number">1.5.</span> <span class="nav-text">bind、call、apply</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%A8%A1%E6%8B%9F-new"><span class="nav-number">1.6.</span> <span class="nav-text">模拟 new</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#axios"><span class="nav-number">1.7.</span> <span class="nav-text">axios</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Promise"><span class="nav-number">1.8.</span> <span class="nav-text">Promise</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#React"><span class="nav-number">1.9.</span> <span class="nav-text">React</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Redux"><span class="nav-number">1.10.</span> <span class="nav-text">Redux</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#new-Vue-%E6%89%A7%E8%A1%8C%E5%90%8E"><span class="nav-number">1.11.</span> <span class="nav-text">new Vue 执行后</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#webpack-%E7%83%AD%E6%9B%B4%E6%96%B0%E5%8E%9F%E7%90%86"><span class="nav-number">1.12.</span> <span class="nav-text">webpack 热更新原理</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%8C%E3%80%81%E7%BD%91%E7%BB%9C%E7%9B%B8%E5%85%B3"><span class="nav-number">2.</span> <span class="nav-text">二、网络相关</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#HTTP-%E7%8A%B6%E6%80%81%E7%A0%81%E5%8F%8A%E5%85%B6%E5%8A%9F%E8%83%BD"><span class="nav-number">2.1.</span> <span class="nav-text">HTTP 状态码及其功能</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#URL-%E8%BE%93%E5%85%A5%E5%88%B0%E9%A1%B5%E9%9D%A2%E5%8A%A0%E8%BD%BD%E7%9A%84%E8%BF%87%E7%A8%8B"><span class="nav-number">2.2.</span> <span class="nav-text">URL 输入到页面加载的过程</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#HTTP-%E8%AF%B7%E6%B1%82%E6%B5%81%E7%A8%8B"><span class="nav-number">2.3.</span> <span class="nav-text">HTTP 请求流程</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#HTTPS-%E5%8E%9F%E7%90%86-%E6%8F%A1%E6%89%8B%E8%BF%87%E7%A8%8B"><span class="nav-number">2.4.</span> <span class="nav-text">HTTPS 原理 (握手过程)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#HTTPS-%E6%8F%A1%E6%89%8B%E6%97%B6%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%AA%8C%E8%AF%81%E8%AF%81%E4%B9%A6"><span class="nav-number">2.5.</span> <span class="nav-text">HTTPS 握手时客户端验证证书</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#HTTP2-%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8"><span class="nav-number">2.6.</span> <span class="nav-text">HTTP2 多路复用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#HTTPS-%E4%B8%AD%E9%97%B4%E4%BA%BA%E6%94%BB%E5%87%BB"><span class="nav-number">2.7.</span> <span class="nav-text">HTTPS 中间人攻击</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#SSL-%E8%BF%9E%E6%8E%A5%E6%96%AD%E5%BC%80%E5%90%8E%E6%81%A2%E5%A4%8D"><span class="nav-number">2.8.</span> <span class="nav-text">SSL 连接断开后恢复</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%89%8D%E7%AB%AF%E9%89%B4%E6%9D%83%E6%96%B9%E6%A1%88"><span class="nav-number">2.9.</span> <span class="nav-text">前端鉴权方案</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%89%E3%80%81%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F"><span class="nav-number">3.</span> <span class="nav-text">三、设计模式</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E7%9A%84%E5%8E%9F%E5%88%99"><span class="nav-number">3.1.</span> <span class="nav-text">设计模式的原则</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F%E3%80%81%E8%AE%A2%E9%98%85-%E5%8F%91%E5%B8%83%E6%A8%A1%E5%BC%8F"><span class="nav-number">3.2.</span> <span class="nav-text">观察者模式、订阅-发布模式</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%84%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99"><span class="nav-number">3.3.</span> <span class="nav-text">面向对象的设计原则</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%AE%BE%E8%AE%A1%E4%B8%80%E4%B8%AA%E7%8E%AF%E5%A2%83"><span class="nav-number">3.4.</span> <span class="nav-text">设计一个环境</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%9B%9B%E3%80%81%E7%AE%97%E6%B3%95%E7%9B%B8%E5%85%B3"><span class="nav-number">4.</span> <span class="nav-text">四、算法相关</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%BF%AD%E4%BB%A3%E6%96%B9%E5%BC%8F%E5%AE%9E%E7%8E%B0-flatten-%E5%87%BD%E6%95%B0"><span class="nav-number">4.1.</span> <span class="nav-text">迭代方式实现 flatten 函数</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%95%B0%E7%BB%84%E7%9A%84%E9%9A%8F%E6%9C%BA%E6%8E%92%E5%BA%8F"><span class="nav-number">4.2.</span> <span class="nav-text">数组的随机排序</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%94%E3%80%81Nodejs"><span class="nav-number">5.</span> <span class="nav-text">五、Nodejs</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%A8%A1%E5%9D%97"><span class="nav-number">5.1.</span> <span class="nav-text">模块</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#require-%E6%A8%A1%E5%9D%97%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6"><span class="nav-number">5.2.</span> <span class="nav-text">require 模块加载机制</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%B8%A4%E4%B8%AA%E6%A8%A1%E5%9D%97%E4%BA%92%E7%9B%B8%E5%BC%95%E7%94%A8"><span class="nav-number">5.3.</span> <span class="nav-text">两个模块互相引用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%BC%82%E6%AD%A5-I-x2F-O"><span class="nav-number">5.4.</span> <span class="nav-text">异步 I&#x2F;O</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Buffer"><span class="nav-number">5.5.</span> <span class="nav-text">Buffer</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Stream"><span class="nav-number">5.6.</span> <span class="nav-text">Stream</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#IPC"><span class="nav-number">5.7.</span> <span class="nav-text">IPC</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%AE%9E%E7%8E%B0%E7%83%AD%E6%9B%B4%E6%96%B0"><span class="nav-number">5.8.</span> <span class="nav-text">实现热更新</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Node-%E6%9B%B4%E9%80%82%E5%90%88%E5%A4%84%E7%90%86-I-x2F-O-%E5%AF%86%E9%9B%86%E5%9E%8B%E4%BB%BB%E5%8A%A1%E8%BF%98%E6%98%AF-CPU-%E5%AF%86%E9%9B%86%E5%9E%8B%E4%BB%BB%E5%8A%A1"><span class="nav-number">5.9.</span> <span class="nav-text">Node 更适合处理 I&#x2F;O 密集型任务还是 CPU 密集型任务</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Node-%E4%B8%BA%E4%BD%95%E4%B8%8D%E7%BB%99%E6%AF%8F%E4%B8%AA-js-%E6%96%87%E4%BB%B6%E4%BB%A5%E7%8B%AC%E7%AB%8B%E7%9A%84%E4%B8%8A%E4%B8%8B%E6%96%87%E6%9D%A5%E9%81%BF%E5%85%8D%E4%BD%9C%E7%94%A8%E5%9F%9F%E8%A2%AB%E6%B1%A1%E6%9F%93"><span class="nav-number">5.10.</span> <span class="nav-text">Node 为何不给每个 js 文件以独立的上下文来避免作用域被污染</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#console-log-%E6%98%AF%E5%90%8C%E6%AD%A5%E8%BF%98%E6%98%AF%E5%BC%82%E6%AD%A5"><span class="nav-number">5.11.</span> <span class="nav-text">console.log 是同步还是异步?</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%88%B6-x2F-%E5%AD%90%E8%BF%9B%E7%A8%8B%E7%9A%84%E6%AD%BB%E4%BA%A1%E6%9C%89%E4%BD%95%E5%BD%B1%E5%93%8D"><span class="nav-number">5.12.</span> <span class="nav-text">父&#x2F;子进程的死亡有何影响</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B"><span class="nav-number">5.13.</span> <span class="nav-text">守护进程</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%B2%98%E5%8C%85%E9%97%AE%E9%A2%98"><span class="nav-number">5.14.</span> <span class="nav-text">粘包问题</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#cookie%E3%80%81session"><span class="nav-number">5.15.</span> <span class="nav-text">cookie、session</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#hosts-%E6%96%87%E4%BB%B6"><span class="nav-number">5.16.</span> <span class="nav-text">hosts 文件</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="chuangges" src="" data-original="/images/chuang.png">
  <p class="site-author-name" itemprop="name">chuangges</p>
  <div class="site-description" itemprop="description">chuangge's blog.</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">39</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">7</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">7</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/chuangges" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;chuangges" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://stackoverflow.com/yourname" title="StackOverflow → https:&#x2F;&#x2F;stackoverflow.com&#x2F;yourname" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-stack-overflow fa-fw"></i>StackOverflow</a>
      </span>
  </div>


  <div class="links-of-blogroll motion-element">
    <div class="links-of-blogroll-title"><i class="fa fa-link fa-fw"></i>
      友情链接
    </div>
    <ul class="links-of-blogroll-list">
        <li class="links-of-blogroll-item">
          <a href="https://juejin.im/" title="https:&#x2F;&#x2F;juejin.im&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">掘金</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://www.nowcoder.com/" title="https:&#x2F;&#x2F;www.nowcoder.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">牛客网</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://www.docschina.org/" title="https:&#x2F;&#x2F;www.docschina.org&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">印记中文</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="http://tool.lu/" title="http:&#x2F;&#x2F;tool.lu&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">在线工具</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://tinypng.com/" title="https:&#x2F;&#x2F;tinypng.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">图片压缩</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://bigjpg.com/" title="https:&#x2F;&#x2F;bigjpg.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">图片放大</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://tool.chinaz.com/" title="https:&#x2F;&#x2F;tool.chinaz.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">站长工具</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://learn-anything.xyz/" title="https:&#x2F;&#x2F;learn-anything.xyz&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">Learn-Anything</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://stackoverflow.com/" title="https:&#x2F;&#x2F;stackoverflow.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">Stack-Overflow</a>
        </li>
    </ul>
  </div>

      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2024</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">chuangge</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-area"></i>
    </span>
    <span title="站点总字数">1m</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
    <span title="站点阅读时长">15:14</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="external nofollow noopener noreferrer" target="_blank">Hexo</a> & <a href="https://pisces.theme-next.org/" class="theme-link" rel="external nofollow noopener noreferrer" target="_blank">NexT.Pisces</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  















  

  

<script src="/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"pluginRootPath":"live2dw/","pluginJsPath":"lib/","pluginModelPath":"assets/","model":{"jsonPath":"/live2dw/assets/tororo.model.json"},"display":{"position":"left","width":180,"height":360},"mobile":{"show":false},"react":{"opacityDefault":1,"opacityOnHover":1},"log":false,"tagMode":false});</script>
        <style>
            [bg-lazy] {
                background-image: none !important;
                background-color: #eee !important;
            }
        </style>
        <script>
            window.imageLazyLoadSetting = {
                isSPA: false,
                preloadRatio: 1,
                processImages: null,
            };
        </script><script>window.addEventListener("load",function(){var t=/\.(gif|jpg|jpeg|tiff|png)$/i,r=/^data:image\/[a-z]+;base64,/;Array.prototype.slice.call(document.querySelectorAll("img[data-original]")).forEach(function(a){var e=a.parentNode;"A"===e.tagName&&(e.href.match(t)||e.href.match(r))&&(e.href=a.dataset.original)})});</script><script>!function(e){e.imageLazyLoadSetting.processImages=t;var n=e.imageLazyLoadSetting.isSPA,i=e.imageLazyLoadSetting.preloadRatio||1,r=o();function o(){var t=Array.prototype.slice.call(document.querySelectorAll("img[data-original]")),e=Array.prototype.slice.call(document.querySelectorAll("[bg-lazy]"));return t.concat(e)}function t(){n&&(r=o());for(var t,a=0;a<r.length;a++)0<=(t=(t=r[a]).getBoundingClientRect()).bottom&&0<=t.left&&t.top<=(e.innerHeight*i||document.documentElement.clientHeight*i)&&function(){var t,e,n,i,o=r[a];e=function(){r=r.filter(function(t){return o!==t})},(t=o).hasAttribute("bg-lazy")?(t.removeAttribute("bg-lazy"),e&&e()):(n=new Image,i=t.getAttribute("data-original"),n.onload=function(){t.src=i,t.removeAttribute("data-original"),e&&e()},t.src!==i&&(n.src=i))}()}function a(){clearTimeout(t.tId),t.tId=setTimeout(t,500)}t(),document.addEventListener("scroll",a),e.addEventListener("resize",a),e.addEventListener("orientationchange",a)}(this);</script></body>
</html>
